﻿using Huirui.Cavan.Domain.MainBoundedContext.PurchaseModule;
using Huirui.Cavan.Domain.MainBoundedContext.SystemModule;
using Huirui.Cavan.Domain.Seedwork;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Huirui.Cavan.Core.Extension;
using Huirui.Cavan.Domain.MainBoundedContext.SalesModule;

namespace Huirui.Cavan.Domain.MainBoundedContext.StockModule
{
    public class StockManager
    {
        private Employee _employee;

        public StockManager(Employee employee)
        {
            this._employee = employee;
        }

        public BoolGenericResult<WareHouse> AddWareHouse(string id, string address, SystemIdentifier.WareHouseStatus status, Department department, Employee employee)
        {
            BoolGenericResult<WareHouse> result = new BoolGenericResult<WareHouse>();

            WareHouse wareHouse = StockModuleFactory.CreateWareHouse(id, address, status, this._employee.UserAccount.Id, this._employee.Corporation, department, employee);

            result.Object = wareHouse;

            return result;
        }

        public BoolGenericResult<WareHouse> UpdateWareHouse(WareHouse oldWareHouse, string address, SystemIdentifier.WareHouseStatus status, Department department, Employee employee)
        {
            BoolGenericResult<WareHouse> result = new BoolGenericResult<WareHouse>();

            oldWareHouse.Address = address;
            oldWareHouse.Status = status;
            oldWareHouse.Department = department;
            oldWareHouse.Employee = employee;

            result.Object = oldWareHouse;

            return result;
        }

        public BoolGenericResult<Stock> AddStock(string id, int nature, string name, string specification, StockType type, StockUnit stockUnit, string barCode, bool isProxy,
            decimal standardPurchasePrice, decimal standardSalesPrice, decimal maxPurchasePrice, decimal minSalesPrice, decimal minLeftNumber, decimal taxRate,
            WareHouse wareHouse, UnitTransfer unitTransfer)
        {
            BoolGenericResult<Stock> result = new BoolGenericResult<Stock>();

            Stock stock = StockModuleFactory.CreateStock(id, nature, name, specification, type, stockUnit, barCode, isProxy,
                standardPurchasePrice, standardSalesPrice, maxPurchasePrice, minSalesPrice, minLeftNumber, taxRate, this._employee.UserAccount.Id,
                this._employee.Corporation, wareHouse, StockIdentifier.StockStatus.Using, StockIdentifier.StockOnSale.OnSale, unitTransfer);

            result.Object = stock;

            return result;
        }

        public BoolGenericResult<Stock> UpdateStock(Stock oldStock, int nature, string name, string specification, StockType type, StockUnit stockUnit, string barCode, bool isProxy, decimal standardPurchasePrice, decimal standardSalesPrice, decimal maxPurchasePrice, decimal minSalesPrice, decimal minLeftNumber, decimal taxRate, WareHouse wareHouse)
        {
            BoolGenericResult<Stock> result = new BoolGenericResult<Stock>();

            oldStock.BarCode = barCode;
            oldStock.IsProxy = isProxy;
            oldStock.MaxPurchasePrice = maxPurchasePrice;
            oldStock.MinSalesPrice = minSalesPrice;
            oldStock.Name = name;
            oldStock.Nature = nature;
            oldStock.Specification = specification;
            oldStock.StandardPurchasePrice = standardPurchasePrice;
            oldStock.StandardSalesPrice = standardSalesPrice;
            oldStock.MinLeftNumber = minLeftNumber;
            oldStock.TaxRate = taxRate;
            oldStock.Type = type;
            oldStock.StockUnit = stockUnit;
            oldStock.WareHouse = wareHouse;

            result.Object = oldStock;

            return result;
        }

        public BoolGenericResult<StockInOutType> AddStockInOutType(string name, bool isIn, Account fromAccount, bool isCostAccounting)
        {
            BoolGenericResult<StockInOutType> result = new BoolGenericResult<StockInOutType>();

            StockInOutType stockInOutType = StockModuleFactory.CreateStockInOutType(name, isIn, fromAccount, isCostAccounting, this._employee.UserAccount.Id, this._employee.Corporation);

            result.Object = stockInOutType;

            return result;
        }

        public BoolGenericResult<StockInOutType> UpdateStockInOutType(StockInOutType oldStockInOutType, string name, bool isIn, Account fromAccount, bool isCostAccounting)
        {
            BoolGenericResult<StockInOutType> result = new BoolGenericResult<StockInOutType>();

            oldStockInOutType.Name = name;
            oldStockInOutType.IsIn = isIn;
            oldStockInOutType.FromAccount = fromAccount;
            oldStockInOutType.IsCostAccounting = isCostAccounting;

            result.Object = oldStockInOutType;

            return result;
        }

        public BoolGenericResult<StockInOut> AddStockIn(string id, DateTime inOutDate, StockInOutType type, string sourceCode, Account fromAccount, string productionNumber, string currency, decimal currencyRate, string remark, string contact, string contactTelephone, Department department, Employee employee, Project project, Supplier supplier, Customer customer, WareHouse wareHouse, IList<StockInOutDetail> details)
        {
            BoolGenericResult<StockInOut> result = new BoolGenericResult<StockInOut>();
            StockInOut stockIn = StockModuleFactory.CreateStockIn(id, inOutDate, type, sourceCode, fromAccount, productionNumber, currency.IsNotNull() ? currency : "RMB", currencyRate > 0 ? currencyRate : 1, remark,
                StockIdentifier.StockInCheckedStatus.UnChecked, this._employee.UserAccount, this._employee.Corporation, department, employee, project, supplier, customer, contact, contactTelephone, wareHouse);

            foreach (StockInOutDetail newStockInDetail in details)
            {
                BoolGenericResult<StockInOutDetail> addStockInDetailResult = this.AddStockInDetail(newStockInDetail.FromAccount, newStockInDetail.Number, newStockInDetail.UnitCost, newStockInDetail.CostType, newStockInDetail.Cost, newStockInDetail.TaxRate, newStockInDetail.DiscountRate,
                    newStockInDetail.Description, newStockInDetail.Stock, newStockInDetail.WareHouse);
                addStockInDetailResult.Object.StockIn = stockIn;
                stockIn.StockInOutDetails.Add(addStockInDetailResult.Object);

                newStockInDetail.Stock.LeftNumber += newStockInDetail.Number;
            }

            result.Object = stockIn;

            return result;
        }

        public BoolGenericResult<StockInOut> AddStockOut(string id, DateTime inOutDate, StockInOutType type, string sourceCode, Account fromAccount, string productionNumber, string currency, decimal currencyRate, string remark, string contact, string contactTelephone, Department department, Employee employee, Project project, Supplier supplier, Customer customer, WareHouse wareHouse, IList<StockInOutDetail> details)
        {
            BoolGenericResult<StockInOut> result = new BoolGenericResult<StockInOut>();
            StockInOut stockIn = StockModuleFactory.CreateStockIn(id, inOutDate, type, sourceCode, fromAccount, productionNumber, currency.IsNotNull() ? currency : "RMB", currencyRate > 0 ? currencyRate : 1, remark,
                StockIdentifier.StockInCheckedStatus.UnChecked, this._employee.UserAccount, this._employee.Corporation, department, employee, project, supplier, null, contact, contactTelephone, wareHouse);

            foreach (StockInOutDetail newStockInDetail in details)
            {
                if (newStockInDetail.Stock.StockByWareHouseReports.FirstOrDefault(s => s.WareHouseCode == wareHouse.Id).LeftNumber < -newStockInDetail.Number)
                {
                    result.IsSuccess = false;
                    result.Messages.Add("出库数量不能大于现有库存数量");
                    return result;
                }

                BoolGenericResult<StockInOutDetail> addStockInDetailResult = this.AddStockInDetail(newStockInDetail.FromAccount, -newStockInDetail.Number, newStockInDetail.UnitCost, newStockInDetail.CostType, newStockInDetail.Cost, newStockInDetail.TaxRate, newStockInDetail.DiscountRate,
                    newStockInDetail.Description, newStockInDetail.Stock, newStockInDetail.WareHouse);
                addStockInDetailResult.Object.StockIn = stockIn;
                stockIn.StockInOutDetails.Add(addStockInDetailResult.Object);

                newStockInDetail.Stock.LeftNumber -= -newStockInDetail.Number;
            }

            result.Object = stockIn;

            return result;
        }

        public BoolGenericResult<StockInOut> UpdateStockIn(StockInOut oldStockIn, DateTime inOutDate, StockInOutType type, Account fromAccount, string productionNumber, string remark, string contact, string contactTelephone, Department department, Employee employee, Project project, Supplier supplier, WareHouse wareHouse, IList<StockInOutDetail> details)
        {
            BoolGenericResult<StockInOut> result = new BoolGenericResult<StockInOut>();
            oldStockIn.InOutDate = inOutDate;
            oldStockIn.Type = type;
            oldStockIn.FromAccount = fromAccount;
            oldStockIn.ProductionNumber = productionNumber;
            oldStockIn.Remark = remark;
            oldStockIn.Department = department;
            oldStockIn.Employee = employee;
            oldStockIn.Supplier = supplier;
            oldStockIn.Contact = contact;
            oldStockIn.ContactTelephone = contactTelephone;
            oldStockIn.WareHouse = wareHouse;

            IList<long> deleted = new List<long>();
            foreach (StockInOutDetail newStockInDetail in details)
            {
                if (newStockInDetail.Id == 0)
                {
                    BoolGenericResult<StockInOutDetail> addStockInDetailResult = this.AddStockInDetail(newStockInDetail.FromAccount, newStockInDetail.Number, newStockInDetail.UnitCost, newStockInDetail.CostType, newStockInDetail.Cost, newStockInDetail.TaxRate, newStockInDetail.DiscountRate,
                        newStockInDetail.Description, newStockInDetail.Stock, newStockInDetail.WareHouse);
                    oldStockIn.StockInOutDetails.Add(addStockInDetailResult.Object);
                    continue;
                }
                if (newStockInDetail.EditUser == -1)
                {
                    deleted.Add(newStockInDetail.Id);
                    continue;
                }
            }
            foreach (StockInOutDetail oldStockInDetail in oldStockIn.StockInOutDetails)
            {
                foreach (StockInOutDetail newStockInDetail in details)
                {
                    if (oldStockInDetail.Id == newStockInDetail.Id)
                    {
                        oldStockInDetail.Cost = newStockInDetail.Cost;
                        oldStockInDetail.CostType = newStockInDetail.CostType;
                        oldStockInDetail.Description = newStockInDetail.Description;
                        oldStockInDetail.FromAccount = newStockInDetail.FromAccount;
                        oldStockInDetail.Number = newStockInDetail.Number;
                        oldStockInDetail.Stock = newStockInDetail.Stock;
                        oldStockInDetail.UnitCost = newStockInDetail.UnitCost;
                        oldStockInDetail.WareHouse = newStockInDetail.WareHouse;
                        oldStockInDetail.TaxRate = newStockInDetail.TaxRate;
                    }
                }
            }
            foreach (long id in deleted)
            {
                StockInOutDetail deletedStockInDetail = oldStockIn.StockInOutDetails.FirstOrDefault(d => d.Id == id);
                oldStockIn.StockInOutDetails.Remove(deletedStockInDetail);
            }


            result.Object = oldStockIn;

            return result;
        }

        public BoolGenericResult<StockInOutDetail> AddStockInDetail(Account fromAccount, decimal number, decimal unitCost, StockIdentifier.CostType costType, decimal cost, decimal taxRate, decimal discountRate, string description, Stock stock, WareHouse wareHouse)
        {
            BoolGenericResult<StockInOutDetail> result = new BoolGenericResult<StockInOutDetail>();
            StockInOutDetail stockInDetail = StockModuleFactory.CreateStockInDetail(fromAccount, number,  unitCost, costType, cost, taxRate, discountRate, description, this._employee.UserAccount.Id, stock, wareHouse);

            result.Object = stockInDetail;

            return result;
        }

        public BoolGenericResult<SalesPrice> AddSalesPrice(string id, string corporationCode, SalesIdentifier.PriceType type, int customerType, DateTime startDate, DateTime? endDate, string remark, IList<SalesPriceDetail> salesPriceDetails, IList<Customer> customers)
        {
            BoolGenericResult<SalesPrice> result = new BoolGenericResult<SalesPrice>();

            SalesPrice salesPrice = SalesModuleFactory.CreateSalesPrice(id, corporationCode, type, customerType, startDate, endDate, remark, DateTime.Now, DateTime.Now, this._employee.UserAccount.Id);

            foreach (Customer c in customers)
            {
                salesPrice.Customers.Add(c);
            }

            foreach (SalesPriceDetail spd in salesPriceDetails)
            {
                SalesPriceDetail salesPriceDetail = SalesModuleFactory.CreateSalesPriceDetail(salesPrice, spd.Stock, spd.UnitCost, spd.DiscountRate, DateTime.Now, DateTime.Now, this._employee.UserAccount.Id);
                salesPrice.SalesPriceDetails.Add(salesPriceDetail);
            }

            result.Object = salesPrice;

            return result;
        }

        public BoolGenericResult<StockAllocate> AllocateStock(string id, DateTime allocateDate, WareHouse outWareHouse, WareHouse inWareHouse, string remark, string productionNumber, StockInOutType inType, StockInOutType outType, IList<StockInOutDetail> stockInOutDetails)
        {
            BoolGenericResult<StockAllocate> result = new BoolGenericResult<StockAllocate>();

            StockInOut stockOut = StockModuleFactory.CreateStockIn(string.Empty, allocateDate, outType, id, null, productionNumber, "RMB", 1, remark, StockIdentifier.StockInCheckedStatus.UnChecked,
    this._employee.UserAccount, this._employee.Corporation, null, this._employee, null, null, null, string.Empty, string.Empty, outWareHouse);

            foreach (StockInOutDetail stockInDetail in stockInOutDetails)
            {
                if (stockInDetail.Stock.StockByWareHouseReports.FirstOrDefault(s => s.WareHouseCode == outWareHouse.Id).LeftNumber < stockInDetail.Number)
                {
                    result.IsSuccess = false;
                    result.Messages.Add("出库数量不能大于现有库存数量");
                    return result;
                }

                BoolGenericResult<StockInOutDetail> addStockOutDetailResult = this.AddStockInDetail(stockInDetail.FromAccount, -stockInDetail.Number, stockInDetail.UnitCost, stockInDetail.CostType, stockInDetail.Cost, stockInDetail.TaxRate, stockInDetail.DiscountRate,
                    stockInDetail.Description, stockInDetail.Stock, outWareHouse);
                addStockOutDetailResult.Object.StockIn = stockOut;
                stockOut.StockInOutDetails.Add(addStockOutDetailResult.Object);
            }

            StockInOut stockIn = StockModuleFactory.CreateStockIn(string.Empty, allocateDate, inType, id, null, productionNumber, "RMB", 1, remark, StockIdentifier.StockInCheckedStatus.UnChecked,
                this._employee.UserAccount, this._employee.Corporation, null, this._employee, null, null, null, string.Empty, string.Empty, inWareHouse);

            foreach (StockInOutDetail stockInDetail in stockInOutDetails)
            {
                BoolGenericResult<StockInOutDetail> addStockInDetailResult = this.AddStockInDetail(stockInDetail.FromAccount, stockInDetail.Number, stockInDetail.UnitCost, stockInDetail.CostType, stockInDetail.Cost, stockInDetail.TaxRate, stockInDetail.DiscountRate,
                    stockInDetail.Description, stockInDetail.Stock, inWareHouse);
                addStockInDetailResult.Object.StockIn = stockIn;
                stockIn.StockInOutDetails.Add(addStockInDetailResult.Object);
            }

            StockAllocate stockAllocate = StockModuleFactory.CreateStockAllocate(id, allocateDate, remark, DateTime.Now, DateTime.Now, this._employee.UserAccount.Id, this._employee.Corporation,
                stockIn, stockOut, productionNumber);

            result.Object = stockAllocate;

            return result;
        }

        public BoolGenericResult<StockCheck> CheckStock(string id, DateTime checkDate, WareHouse wareHouse, string productionNumber, string remark, IList<StockCheckDetail> stockCheckDetails)
        {
            BoolGenericResult<StockCheck> result = new BoolGenericResult<StockCheck>();

            StockCheck stockCheck = StockModuleFactory.CreateStockCheck(id, checkDate, wareHouse, productionNumber, remark, DateTime.Now, DateTime.Now, this._employee.UserAccount.Id, this._employee.Corporation);

            foreach (StockCheckDetail scd in stockCheckDetails)
            {
                StockCheckDetail stockCheckDetail = StockModuleFactory.CreateStockCheckDetail(stockCheck, scd.Stock, scd.CheckNumber, DateTime.Now, DateTime.Now, this._employee.UserAccount.Id);
                stockCheck.StockCheckDetails.Add(stockCheckDetail);
            }
            result.Object = stockCheck;

            return result;
        }

        public BoolGenericResult<UnitTransfer> AddUnitTransfer(StockUnit fromUnit, StockUnit toUnit, decimal fromNumber, decimal toNumber)
        {
            BoolGenericResult<UnitTransfer> result = new BoolGenericResult<UnitTransfer>();

            UnitTransfer unitTransfer = StockModuleFactory.CreateUnitTransfer(this._employee.Corporation, DateTime.Now, DateTime.Now, this._employee.UserAccount.Id,
                fromUnit, toUnit, fromNumber, toNumber);

            result.Object = unitTransfer;

            return result;
        }

        public BoolGenericResult<StockInitialBalance> AddStockInitialBalance(Stock stock, WareHouse wareHouse, DateTime initialDate, decimal number,
            decimal unitCost)
        {
            BoolGenericResult<StockInitialBalance> result = new BoolGenericResult<StockInitialBalance>();
            StockInitialBalance stockInitialBalance = StockModuleFactory.CreateStockInitialBalance(stock, wareHouse, initialDate, number, unitCost, this._employee.Corporation, DateTime.Now, DateTime.Now, this._employee.UserAccount.Id);
            stock.LeftNumber += number;

            result.Object = stockInitialBalance;

            return result;
        }

        public BoolGenericResult<StockType> AddStockType(string name, int edituser)
        {
            BoolGenericResult<StockType> result = new BoolGenericResult<StockType>();

            StockType stockType = StockModuleFactory.CreateStockType(name,edituser);
            result.Object = stockType;
            return result;
        }

        public BoolGenericResult<StockType> UpdateStockType(StockType oldstocktype,string name, int edituser)
        {
            BoolGenericResult<StockType> result = new BoolGenericResult<StockType>();
            oldstocktype.Name = name;
            oldstocktype.EditUser = edituser;
            oldstocktype.ModifyDate = DateTime.Now;
            result.Object = oldstocktype;
            return result;
        }
    }
}
